﻿/*

========================================================================

	                        Light Prepass Renderer

		                        by Floris Groen

                         E-mail : fmfgroen@yahoo.com
                     Blog : www.florisgroen.blogspot.com
		      

========================================================================

*/

using Microsoft.Xna.Framework;

namespace LightPrePassRenderer.Engine
{
    public class Camera
    {
        public Camera()
        {
            Position = new Vector3(0, 0, 10);
            Direction = new Vector3(0, 0, 1);
            Up = Vector3.Up;

            FieldOfView = MathHelper.ToRadians(45.0f);
            AspectRatio = 1280.0f / 720.0f;
            NearPlane = 0.1f;
            FarPlane = 100.0f;

            BoundingFrustum = new BoundingFrustum(view * projection);

            Update();
        }

        public Vector3 Position
        {
            get;
            set;
        }

        Vector3 direction;

        public Vector3 Direction
        {
            get { return direction; }
            set { direction = Vector3.Normalize(value); }
        }

        public Vector3 Up
        {
            get;
            set;
        }

        public float FieldOfView
        {
            get;
            set;
        }

        public float AspectRatio
        {
            get;
            set;
        }

        public float NearPlane
        {
            get;
            set;
        }

        public float FarPlane
        {
            get;
            set;
        }

        Matrix view;
        Matrix projection;

        public void GetView(out Matrix view)
        {
            view = this.view;
        }

        public void GetProjection(out Matrix projection)
        {
            projection = this.projection;
        }

        public BoundingFrustum BoundingFrustum
        {
            get;
            private set;
        }

        /// <summary>
        /// Moves the camera to the left or right.
        /// </summary>
        /// <param name="speed">Amount to move. Use a negative value to move left.</param>
        public void Strafe(float speed)
        {
            Vector3 Right = Vector3.Cross(direction, Up);
            Position += Right * speed;

            Update();
        }

        /// <summary>
        /// Moves the camera forward or backwards along its viewing direction vector.
        /// </summary>
        /// <param name="speed">Amount to move. Use a negative value to move backwards.</param>
        public void Move(float speed)
        {
            Position += direction * speed;

            Update();
        }

        /// <summary>
        /// Rotates the camera left or right.
        /// </summary>
        /// <param name="angle">Radians to rotate.</param>
        public void Pan(float angle)
        {
            direction = Vector3.Transform(direction, Quaternion.CreateFromAxisAngle(Up, angle));

            Update();
        }

        /// <summary>
        /// Rotates the camera up or down. Note that this camera does not handle upside down situations.
        /// </summary>
        /// <param name="angle">Radians to rotate.</param>
        public void Tilt(float angle)
        {
            var right = Vector3.Cross(direction, Up);

            direction = Vector3.Transform(direction, Quaternion.CreateFromAxisAngle(right, angle));

            Update();
        }

        /// <summary>
        /// Updates the view and projections matrices. Call this after changing any property of the camera.
        /// </summary>
        public void Update()
        {
            view = Matrix.CreateLookAt(Position, Position + Direction, Up);
            projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
            BoundingFrustum.Matrix = view * projection;
        }
    }
}
